#include <iostream>
#include <stdlib.h>
#include <string>
#include "Lista.h"
#include "Entity3D.h"
#include "Player.h"
#include "Enemy.h"
using namespace std;

// Opciones posibles para la gestión de listas
enum ENUM_OPCIONES {INSERTAR = 1, MODIFICAR = 2, BORRAR = 3, MOSTRAR = 4, SALIR = 5};
// Listas posibles a gestionar
enum ENUM_LISTAS {ENTITY_3D = 1, PLAYER = 2, ENEMY = 3, SALIR_TOTAL = 4};

/**
  * Limpia la pantalla controlando que pueda producirse una excepción, dado que solo funcionará en sistemas UNIX.
  * Si falla, no daremos error, sino que continuaremos sin pintar la pantalla
  */
const void limpiaPantalla() {
	try {
		system("clear");
	} catch (...) {
		// No hacemos nada
	}
}

/**
 * Dibuja el menú y devuelve la opción introducida, parseada a entero.
 * Si la cadena introducida no corresponde a un entero, devolverá 0
 */
const int dibujaMenu() {
	bool bRecargar = true;
	string sOpcion;

	// Borramos la pantalla
	limpiaPantalla();

	// Mostramos el menú al usuario para que seleccione una acción 
	// a realizar con la lista seleccionada en un paso anterior
	cout << "---------------- Menú -------------" << endl;
	cout << endl;
	cout << "(1)    Insertar" << endl;
	cout << "(2)    Modificar" << endl;
	cout << "(3)    Borrar" << endl;
	cout << "(4)    Mostrar" << endl;
	cout << "(5)    Salir" << endl;
	cout << endl;
	cout << "Seleccione opción: ";
	cin >> sOpcion;
	cout << endl;

	// Recuperamos el entero asociado a la cadena introducida
	return atoi(sOpcion.c_str());
}

/**
 * Dibuja el menú principal y devuelve la opción introducida, parseada a entero.
 * Si la cadena introducida no corresponde a un entero, devolverá 0
 */
const int dibujaMenuPrincipal() {
	bool bRecargar = true;
	string sOpcion;

	// Borramos la pantalla
	limpiaPantalla();

	// Mostramos el menú al usuario para que seleccione la 
	// lista sobre la que quiere interactuar
	cout << "---------------- Menú principal -------------" << endl;
	cout << endl;
	cout << "(1)    Gestionar Lista Entity3D" << endl;
	cout << "(2)    Gestionar Lista Player" << endl;
	cout << "(3)    Gestionar Lista Enemy" << endl;
	cout << "(4)    Salir" << endl;
	cout << endl;
	cout << "Seleccione opción: ";
	cin >> sOpcion;
	cout << endl;

	// Recuperamos el entero asociado a la cadena introducida
	return atoi(sOpcion.c_str());
}

/**
  * Lee una entidad dependiendo del tipo de entidad, que vendrá determinado 
  * por la opción recibida (corresponde al enumerado ENUM_LISTAS)
  */
Entity3D& leerEntidad(int opcion) {
	Entity3D *p;
	string sX, sY, sZ, sName, sScore, sRange;

	// Solicita al usuario que rellene los datos correspondientes a la entidad 
	// (dicha entidad podrá ser un Entity3D, Player o Enemy, dependiendo de la 
	// lista que se está gestionando)
	cout << "*** Posición3D" << endl;
	cout << "    X: ";
	cin >> sX;
	cout << "    Y: ";
	cin >> sY;
	cout << "    Z: ";
	cin >> sZ;
	cout << "*** Nombre: ";
	cin >> sName;

	// En función de las entidades manejadas por la lista que se está gestionando se solicitarán más o menos datos
	switch (opcion) {
		case ENTITY_3D:
			p = new Entity3D(atoi(sX.c_str()), atoi(sY.c_str()), atoi(sZ.c_str()), sName);
			break;
		case PLAYER:
			cout << "*** Score: ";
			cin >> sScore;

			p = new Player(atoi(sX.c_str()), atoi(sY.c_str()), atoi(sZ.c_str()), sName, atoi(sScore.c_str()));
			break;
		case ENEMY:
			cout << "*** Range: ";
			cin >> sRange;

			p = new Enemy(atoi(sX.c_str()), atoi(sY.c_str()), atoi(sZ.c_str()), sName, atoi(sRange.c_str()));
			break;
		default:
			break;
	}

	return *p;
}

/**
 * Main
 */
int main(void) {
 	// Listas que manejaremos
	Lista<Entity3D> iLista;
 	Lista<Player> iListaPlayer;
 	Lista<Enemy> iListaEnemy;

	// Variables para el manejo de los menús
	int iOpcion, iOpcionMenuPrincipal;

	// Posición a tratar de la lista
	string sPosicion;
	
	// Puntero a un Entity3D que nos servirá para leer los distintos tipos de entidades que se pueden introducir
	Entity3D *pEnt;

	try {
		do {
			iOpcionMenuPrincipal = dibujaMenuPrincipal();

			if (iOpcionMenuPrincipal && iOpcionMenuPrincipal != SALIR_TOTAL
					&& (iOpcionMenuPrincipal == ENTITY_3D
							|| iOpcionMenuPrincipal == PLAYER
							|| iOpcionMenuPrincipal == ENEMY)) {
				do {
					iOpcion = dibujaMenu();
					/**
					 * En función de la opción seleccionada determinamos la funcionalidad a realizar
					 */
					switch (iOpcion) {
					case INSERTAR:
						cout << "Posición en la lista: ";
						cin >> sPosicion;

						pEnt = &leerEntidad(iOpcionMenuPrincipal);
						switch (iOpcionMenuPrincipal) {
							case ENTITY_3D:
								iLista.insertar(atoi(sPosicion.c_str()), *pEnt);
								break;
							case PLAYER:
								iListaPlayer.insertar(atoi(sPosicion.c_str()), (Player&)*pEnt);
								break;
							case ENEMY:
								iListaEnemy.insertar(atoi(sPosicion.c_str()), (Enemy&)*pEnt);
								break;
							default:
								break;
						}
						break;
					case MODIFICAR:
						cout << "Posición en la lista: ";
						cin >> sPosicion;

						pEnt = &leerEntidad(iOpcionMenuPrincipal);
						switch (iOpcionMenuPrincipal) {
							case ENTITY_3D:
		 						iLista.modificar(atoi(sPosicion.c_str()), *pEnt);
								break;
							case PLAYER:
		 						iListaPlayer.modificar(atoi(sPosicion.c_str()), (Player&)*pEnt);
								break;
							case ENEMY:
								iListaEnemy.modificar(atoi(sPosicion.c_str()), (Enemy&)*pEnt);
								break;
							default:
								break;
						}
						break;
					case BORRAR:
						cout << "Posición en la lista: ";
						cin >> sPosicion;

						switch (iOpcionMenuPrincipal) {
							case ENTITY_3D:
								iLista.borrar(atoi(sPosicion.c_str()));
								break;
							case PLAYER:
								iListaPlayer.borrar(atoi(sPosicion.c_str()));
								break;
							case ENEMY:
								iListaEnemy.borrar(atoi(sPosicion.c_str()));
								break;
							default:
								break;
						}
						break;
					case MOSTRAR:
						switch (iOpcionMenuPrincipal) {
							case ENTITY_3D:
								iLista.mostrar();
								break;
							case PLAYER:
								iListaPlayer.mostrar();
								break;
							case ENEMY:
								iListaEnemy.mostrar();
								break;
							default:
								break;
						}

						break;
					case SALIR:
						break;
						// Si no es ninguno de los valores permitidos
					default:
						// La opción es inválida, por tanto la marcamos
						// a cero para posteriores tratamientos de dicha opción
						iOpcion = 0;
						break;
					}

					/*
					 * Si la opción es válida y no es SALIR
					 */
					if (iOpcion && iOpcion != SALIR) {
						cout << endl << "<Pulse intro para continuar>" << endl;
						// No conseguía que con un único "cin.get();" se detuviese el flujo
						cin.get();
						cin.get();
					}
				// Repetimos el bucle mientras el usuario no seleccione SALIR
				} while (iOpcion != SALIR);

			}// Fin de la comprobación de la opción del menú introducida

			// Limpiamos las listas al volver al menú principal
			iListaPlayer.~Lista();
			iLista.~Lista();

		} while (iOpcionMenuPrincipal != SALIR_TOTAL);

	} catch (ExcepcionBasica &e) {
		// Si se produce alguna excepción destruimos las listas
		iListaPlayer.~Lista();
		iLista.~Lista();

		// Visualizamos la excepción producida (que tiene sobreescrito el operador "<<")
		cerr << e << endl;
	}

	return 0;
}

